#include "presentation.hpp"
#include "figure.hpp"
#include "inline.hpp"
#include "video.hpp"

#define BUFSIZE 2048

//------------------------------XML-HANDLER-------------------------------//

void start_element(void* pclass, const char *tag, const char **attr){
	((xlider_presentation*)pclass)->start(tag,attr);
}

void end_element(void *pclass, const char *tag){
	((xlider_presentation*)pclass)->end(tag);
}

void text_content(void *pclass, const char* content, int len){
	((xlider_presentation*)pclass)->fetch(content,len);
}

void start_cdata(void* pclass){
	((xlider_presentation*)pclass)->c_data=true;
}

void end_cdata(void* pclass){
	((xlider_presentation*)pclass)->c_data=false;
}

//------------------------PRESENTATION-SLIDES-----------------------------//

xlider_presentation::xlider_presentation(string pf){
	filename=pf;
	assert(file_exist(filename),"file does not exist", filename);
	param.set_prefix("$");
	c_data=false;
	slide_found=false;
	n_slide_ready=0;
	parser=NULL;
	slide_idx=0;
	current_item=0;
	
	// find configuration file
	string cf(getenv("HOME"));
	cf.append("/.xliderconf");
	if(!file_exist(cf))cf.assign("/etc/xliderconf");
	if(!file_exist(cf))cf.assign("/usr/local/etc/xliderconf");
	if(!file_exist(cf)) THROW("can not find xliderconf");
	
	xml_config_reader xonf(cf.c_str());
	home_path.assign(xonf.GetString("xlider.home"));
	background_scale=1.0;
	slide_to_compile="all";
}

void xlider_presentation::start(const char *tag, const char **attr){
	stag.push(tag);	
	if(stag.top()=="presentation"){
		if(stag.size()>1)throw "miss-placed <presentation> tag";
		parsing_start=true;
	}
	if(!parsing_start) return;
	translate_param(tag,attr);

	if(stag.top()=="presentation") get_presentation_param();
	else if(stag.top()=="slide") prepare_slide();
	else {
		item_index_stack.push(current_item);
		current_item=item.size();
		if(stag.top()=="figure") prepare_figure();
		else if(stag.top()=="inline") prepare_inline();
		else if(stag.top()=="movie") prepare_movie();
		else prepare_item();
	}
}

void xlider_presentation::fetch(const char* content, int len){
	// here the buffer is made null terminated
	char cstr[len+1];
	memset(cstr,0,len+1);
	memcpy(cstr,content,len);
	if(stag.size()>1){
		if(stag.top()=="slide") fetch_slide(cstr);
		else fetch_item(cstr);
	}
}

void xlider_presentation::end(const char *tag){
	if(compile_this_slide&&stag.size()>1){
		if(stag.top()=="slide") create_slide();
		else {
			create_item();
			current_item=item_index_stack.top();
			item_index_stack.pop();
		}
		n_slide_ready++;
	}	
	stag.pop();
}

void xlider_presentation::translate_param(const char *tag, const char **attr){
	param.clear();
	for(int n=0;attr[n];n+=2){
		param.push_pair(attr[n],attr[n+1]);
	}
}

void xlider_presentation::read_theme(){
	background_color=theme.PeekString("theme.background.color");
	background_image=theme.PeekString("theme.background.image");
	if(theme.Exist("theme.background.scale"))
		background_scale=theme.GetDouble("theme.background.scale");
	margin_top=theme.GetDouble("theme.layout.margin.top");
	margin_left=theme.GetDouble("theme.layout.margin.left");
	margin_right=theme.GetDouble("theme.layout.margin.right");
}

// read theme and overide with parameter
void xlider_presentation::read_theme_overide(){
	read_theme();	
	if(param.exist("bgcolor"))background_color=param.string_value("bgcolor");
	if(param.exist("bgimage"))background_image=param.string_value("bgimage");
	if(param.exist("bgscale"))background_scale=param.double_value("bgscale");	
	if(param.exist("lmargin"))margin_left=param.double_value("lmargin");
	if(param.exist("tmargin"))margin_top=param.double_value("tmargin");
	if(param.exist("rmargin"))margin_right=param.double_value("rmargin");
	if(margin_left<1.0)margin_left*=slide_width;
	if(margin_right<1.0)margin_right*=slide_width;
	if(margin_top<1.0)margin_top*=slide_height;
}

void xlider_presentation::get_presentation_param(){
	string sth=param.string_value("theme");
	if(sth!="") theme.Read(get_themepath(sth));
	else theme.Read(get_themepath("default"));
	
	// Take params...
	if(param.exist("width")) slide_width=param.double_value("width");
	else slide_width=1024.0;

	if(param.exist("height")) slide_height=param.double_value("height");
	else slide_height=768.0;
	if(param.exist("interaction")) inter_script=param.string_value("interaction");
	else inter_script="interaction";
}

void xlider_presentation::prepare_slide(){
	if(stag.size()!=2) THROW("misplaced slide");
	// clearence
	item.clear();
	slide_idx=slide_names.size();
	string str_name;
	
	if(param.exist("name")) {
		str_name=param.string_value("name");
		slide_names.push_back(str_name);
	} else {
		char newname[256];
		sprintf(newname, "slide-%d", slide_idx);
		str_name.assign(newname);
		slide_names.push_back(str_name);
	}

//	DEBUGSTR("start slide",slide_names.back().c_str());

	compile_this_slide=false;
	if(slide_to_compile=="all") {
		compile_this_slide=true;
		slide_found=true;
	} else {
		if(slide_to_compile==str_name) slide_found=true;
	}
	if(slide_to_compile==slide_names.back()) compile_this_slide=true;

	if(!compile_this_slide){
		DEBUGSTR("skipped", slide_names.back().c_str());
		slide_names.back().assign("");
	} else
		DEBUGSTR("processing slide", slide_names.back().c_str());

	// a slide may have its own script
	// not used yet
	if(param.exist("script")) slide_script=param.string_value("script");
	else slide_script="slide";

	read_theme_overide();
}

void xlider_presentation::fetch_slide(char* cstr){}

// items are new created here....
void xlider_presentation::prepare_item(){	
	char sname[128];
	sprintf(sname, "item-%d", current_item);
	int ilevel=stag.size()-3;
	if(ilevel>2)ilevel=2; // accept only 3 levels
	if( !theme.Exist((string("theme.")+stag.top()).c_str() )) stag.top()="item";
	item_data* ni=new item_data(sname, ilevel, this);
	ni->read_theme(theme,param,stag.top());
	item.push_back(ni);

}

void xlider_presentation::prepare_inline(){
	char sname[128];
	sprintf(sname, "item-%d", current_item);
	int ilevel=stag.size()-3;
	if(ilevel>2)ilevel=2; // accept only 3 levels
	inline_text* ni=new inline_text(sname, ilevel, this);
	ni->read_theme(theme,param,stag.top());
	item.push_back(ni);
}

void xlider_presentation::prepare_figure(){
	char sname[128];
	sprintf(sname,"item-%d",current_item);
	figure* newfigure=new figure(sname,this);
	newfigure->read_theme(theme,param,stag.top());
	item.push_back(newfigure);
}

void xlider_presentation::prepare_movie(){
	char sname[128];
	sprintf(sname,"item-%d",current_item);
	video* newvideo=new video(sname,this);
	newvideo->read_theme(theme,param,stag.top());
	item.push_back(newvideo);
}

void xlider_presentation::fetch_item(char* cstr){item[current_item]->text.append(cstr);}

void xlider_presentation::create_item(){trim(item[current_item]->text);}

void xlider_presentation::compile(string slide){
	char buf[BUFSIZE];
	slide_to_compile=slide;

	parser=XML_ParserCreate(NULL);
	XML_SetUserData(parser, this);
	XML_SetElementHandler(parser, start_element, end_element);
	XML_SetCharacterDataHandler(parser,text_content);
	XML_SetCdataSectionHandler(parser,start_cdata,end_cdata);

 	ifstream xmlinput(filename.c_str());
 	int len,done;

	int cnt=0;
	 
	do {
		xmlinput.read(buf,BUFSIZE);
		len=xmlinput.gcount();
		done=len<BUFSIZE;
		if (!XML_Parse(parser, buf, len, done))error_handler();
	}while(!done);

	XML_ParserFree(parser);
	
	// FIXME! don't do this if slide name is not found!!
	if (n_slide_ready>0) {
		combine_slides();
	} else {
		if(slide=="all") {
			// FIXME! implement die!
			die("failed compiling the source file. May be uncomplete source.");
		} else {
			die("can not find slide", slide);
		}
	}
}

void xlider_presentation::error_handler() {
	char serr[512];
	XML_Error errno=XML_GetErrorCode(parser);
	int lino=XML_GetCurrentLineNumber(parser);	
	sprintf(serr,"Error: %s (%d) at line %d \n",XML_ErrorString(errno),errno,lino);
	THROW(serr);
}
